/*
 *  clusterfusion.cpp
 *  bgsubtraction
 *
 *  Created by a1gucis on 4/20/09.
 *  Copyright 2009 __MyCompanyName__. All rights reserved.
 *
 */

#include "clusterfusion.h"

ClusterFusion::ClusterFusion() {
	this->bgLabel = 0;
	tmpClusters = new Clusters();
}

ClusterFusion::~ClusterFusion() {
	
}

void ClusterFusion::fuse(IplImage *image, int iterations) {
	cvMorphologyEx(image, image, 0, 0, CV_MOP_CLOSE, iterations); 
	cvMorphologyEx(image, image, 0, 0, CV_MOP_OPEN, iterations);

}
void ClusterFusion::fuse(IplImage *image, int bgLabel, int neighbourhood) {
	this->image = image;
	this->bgLabel = bgLabel;
	for (int i=0;i<image->height/neighbourhood;i++) {
		for (int j=0;j<image->width/neighbourhood;j++) {
			tmpClusters->clear();
			formCluster(i, j, neighbourhood, tmpClusters);
			int maxLabel = tmpClusters->getMaxLabel();
			if (maxLabel != -1)
				substituteCluster(i, j, neighbourhood, maxLabel);
		}
	}
	
}

void ClusterFusion::formCluster(int row, int col, int neighbourhood, Clusters *clusters) {
	uchar *data = (uchar *)image->imageData;
	data += (row*neighbourhood)*image->widthStep+(col*neighbourhood)*image->nChannels; //image[row*neighbourhood][col*neighbourhood]
	for (int i=0;i<neighbourhood;i++) {
		for (int j=0;j<neighbourhood;j++) {
			int label = data[i*image->widthStep+j*image->nChannels];
			if (label != bgLabel)
				clusters->addLabel(label);
		}
	}
}

void ClusterFusion::substituteCluster(int row, int col, int neighbourhood, int maxLabel) {
	uchar *data = (uchar *)image->imageData;
	data += (row*neighbourhood)*image->widthStep+(col*neighbourhood)*image->nChannels; //image[row*neighbourhood][col*neighbourhood]
	for (int i=0;i<neighbourhood;i++) {
		for (int j=0;j<neighbourhood;j++) {
			if (data[i*image->widthStep+j*image->nChannels] != bgLabel)
				data[i*image->widthStep+j*image->nChannels] = maxLabel;
		}
	}
	
}

//****old techniques
bool ClusterFusion::checkWhetherFusion(int row, int col, int oldLabel, int neighbourhood) {
	bool top = false, bottom = false, right = false, left = false;
	uchar *data = (uchar *)image->imageData;
	
	for (int i=0;i<neighbourhood;i++) {
		//check for top neighbourhood
		if (!top) {
			if (row-i >= 0) {
				int pixelVal = (int)data[(row-i)*image->widthStep+col*image->nChannels]; // image[row-i][col]
				if ((pixelVal == oldLabel) || (pixelVal == bgLabel))
					top = true;
			} else
				top = true;
		}
		
		//check for bottom neighbourhood
		if (!bottom) {
			if (row+i < image->height) {
				int pixelVal = (int)data[(row+i)*image->widthStep+col*image->nChannels]; //image[row+i][col]
				if ((pixelVal == oldLabel) || (pixelVal == bgLabel))
					bottom = true;
			} else
				bottom = true;
		}
		
		//check for left neighbourhood
		if (!left) {
			if (col-i >= 0) {
				int pixelVal = (int)data[row*image->widthStep+(col-i)*image->nChannels]; //image[row][col-i]
				if ((pixelVal == oldLabel) || (pixelVal == bgLabel))
					left = true;
			} else
				left = true;
		}
		
		//check for right neighbourhood
		if (!right) {
			if (col+i < image->width) {
				int pixelVal = (int)data[row*image->widthStep+(col+i)*image->nChannels]; //image[row][col+i]
				if ((pixelVal == oldLabel) || (pixelVal == bgLabel))
					right = true;
			} else
				right = true;
		}
	}
	
	if (top && bottom && left && right)
		return true;
	else
		return false;
	
}

void ClusterFusion::fuseCluster(int row, int col, int oldLabel) {
	uchar *data = (uchar *)image->imageData;
	int label = (int)data[row*image->widthStep+col*image->nChannels]; //image[row][col]
	bool terminate = false;
	int leftCol = col;
	int rightCol = col+1;
	while (!terminate) {
		terminate = true;
		leftCol = col;
		rightCol = col+1;
		if (row != image->height) {
			//direction: left
			if (data[row*image->widthStep+leftCol*image->nChannels] == label) { //image[row][col]
				terminate = false;
				while (data[row*image->widthStep+leftCol*image->nChannels] == label) {
					data[row*image->widthStep+leftCol*image->nChannels] = oldLabel;
					leftCol--;
					if (leftCol < 0)
						break;
				}
			}
			
			//direction: right
			if (rightCol < image->width) {
				if (data[row*image->widthStep+rightCol*image->nChannels] == label) {
					terminate = false;
					while (data[row*image->widthStep+rightCol*image->nChannels] == label) {
						data[row*image->widthStep+rightCol*image->nChannels] = oldLabel;
						rightCol++;
						if (rightCol == image->width)
							break;
					}
				}
			}
		}
		row++;
	}
}

/*void ClusterFusion::fuse(IplImage *image, int bgLabel) {
	this->image = image;
	this->bgLabel = bgLabel;
	uchar *data = (uchar *)image->imageData;
	int oldLabel = 0;
	bool initialized = false;
	int count = 0;
	int countNr = 0;
	for (int i=0;i<image->height;i++) {
		for (int j=0;j<image->width;j++) {
			if ((!initialized) && ((int)data[i*image->widthStep+j*image->nChannels]) != bgLabel) {
				cout<<"YA"<<endl;
				oldLabel = (int)data[i*image->widthStep+j*image->nChannels];
				initialized = true;
			}
			if(initialized)
			if ((oldLabel != data[i*image->widthStep+j*image->nChannels]) && (data[i*image->widthStep+j*image->nChannels] != bgLabel)) {
				if (checkWhetherFusion(i, j, oldLabel, CLUST_NEIGHBOURHOOD)) {
					cout<<"OldLabel: "<<oldLabel<<endl;
					cout<<"BGLabel: "<<bgLabel<<endl;
					cout<<"data: "<<(int)data[i*image->widthStep+j*image->nChannels]<<endl;
					fuseCluster(i, j, oldLabel);
					cout<<"CLUSTER AT: "<<i<<","<<j<<" FUSED"<<endl;
					count++;
				}
				else
					oldLabel = data[i*image->widthStep+j*image->nChannels];
			}
			if (count > countNr)
				break;
		}
		if (count > countNr)
			break;
		
	}
}*/


//debug purposes
void ClusterFusion::test() {
	IplImage *image = cvLoadImage("./simpson2Gray.jpg");
	IplImage *gray = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1);
	Clusters *clusters = new Clusters();
	cvCvtColor(image, gray, CV_BGR2GRAY);
	//cvSaveImage("./simpson2Gray.jpg", gray);
	this->image = gray;
	this->bgLabel = 255;
	//this->formCluster(18, 65, CLUST_NEIGHBOURHOOD, clusters);
	//this->substituteCluster(18, 65, CLUST_NEIGHBOURHOOD, 255);
	//cout<<"Max label: "<<clusters->getMaxLabel()<<endl;
	this->fuse(gray, 255, 9);
	/*if (checkWhetherFusion(6, 185, 0, 30)) {
		cout<<"TRUE"<<endl;
		fuseCluster(6, 185, 0);
	}*/
	cvNamedWindow("Test", CV_WINDOW_AUTOSIZE);
	cvShowImage("Test", this->image);
	
	while(true) {
		char c = cvWaitKey(25);
		if (c == 27)
			break;
	}
	cvDestroyWindow("Test");
	//70 198
	
}